import numpy as np
import wx

# 简易ui框架
class UI():
	def __init__(self, parent):
		self.parent = parent
		self.group = {}
	def Box(self, align, list, flag=0, border=0):
		sizer = wx.BoxSizer(align)
		# 遍历list
		for i in range(len(list)):
			element = list[i]
			sizer.Add(element[0], flag=element[1], border=element[2])
		return (sizer, flag, border)
	def Grid(self, row, col, vgap, hgap, list, flag=0, border=0):
		sizer = wx.GridSizer(row, col, vgap, hgap)
		# 遍历list
		for i in range(len(list)):
			element = list[i]
			sizer.Add(element[0], flag=element[1], border=element[2])
		return (sizer, flag, border)
	def BorderBox(self, name, size, align, list, flag=0, border=0):
		border_box = wx.StaticBox(self.parent, -1, name, size=size)
		sizer = wx.StaticBoxSizer(border_box)
		box = wx.BoxSizer(align)
		sizer.Add(box, 0, wx.ALL, 10)
		# 遍历list
		for i in range(len(list)):
			element = list[i]
			box.Add(element[0], flag=element[1], border=element[2])
		return (sizer, flag, border)
	def Label(self, text, var={}, flag=0, border=0, size=wx.DefaultSize, bind=None):
		def replace(text):
			for key in var:
				# 区分key是字符串则替换{s:key}，否则替换{d:key}
				if isinstance(var[key].value, str):
					text = text.replace("{s:"+key+"}", var[key].value)
				else:
					text = text.replace("{d:"+key+"}", str(var[key].value))
			return text
		node = wx.StaticText(self.parent, label=replace(text), size=size)
		if bind:
			bind.bind(lambda value: node.SetLabelText(str(value)))
		if var:
			# 遍历数组
			for key in var:
				var[key].bind(lambda value: node.SetLabelText(replace(text)))
		return (node, flag, border)
	def Button(self, label, onclick=None, size=(120,30),flag=0, border=0):
		node = wx.Button(self.parent, label=label, size=size)
		if onclick:
			node.Bind(wx.EVT_BUTTON, onclick)
		return (node, flag, border)
	def TextEntry(self, value="", onchange=None, onload=None, size=(80,-1), flag=0, border=0, style=0, bind=None):
		def _getvalue():
			if style & wx.TE_PROCESS_ENTER:
				v = node.GetValue()
				if v=="":
					return None
				else:
					return int(node.GetValue())
			else:
				return node.GetValue()
		def _onchange(evt):
			if onchange:
				onchange(evt)
			if bind:
				bind.value = _getvalue()
		def _onbindchange(value):
			if value != _getvalue():
				node.SetValue(str(value))

		node = wx.TextCtrl(self.parent, size=size, value=value, style=style)
		node.Bind(wx.EVT_TEXT, _onchange)
		if onload:
			onload(node)
		if bind:
			node.SetValue(str(bind.value))
			bind.bind(_onbindchange)
		return (node, flag, border)
	def CheckBox(self, label="", group=None, ontoggle=None, flag=0, border=0, bind=None):
		def _ontoggle(evt):
			if ontoggle:
				ontoggle(evt)
			if bind:
				bind.value = node.GetValue()
			if group:
				for v in self.group[group]:
					if v != node:
						v.SetValue(False)
		node = wx.CheckBox(self.parent, label=label)
		if group != None:
			if self.group.get(group) == None:
				self.group[group] = []
			self.group[group].append(node)
		node.Bind(wx.EVT_CHECKBOX, _ontoggle)
		if bind:
			node.SetValue(bind.value)
			bind.bind(lambda value: node.SetValue(value))
		return (node, flag, border)
	def Slider(self,value=0,minValue=0,maxValue=100,size=wx.DefaultSize,style=None, onchange=None,flag=0, border=0, bind=None):
		def _onchange(evt):
			if onchange:
				onchange(evt)
			if bind:
				bind.value = node.GetValue()
		node = wx.Slider(self.parent, value=value, minValue=minValue, maxValue=maxValue, size=size, style=style)
		node.Bind(wx.EVT_SLIDER, _onchange)
		if bind:
			node.SetValue(bind.value)
			bind.bind(lambda value: node.SetValue(int(value)))
		return (node, flag, border)
	def Bitmap(self, rgb=None, size=wx.DefaultSize, bgColor=wx.Colour(0,0,0,0), flag=0, border=0, bind=None):
		def _setBipmap(value):
			node.SetBitmap(wx.Bitmap.FromBuffer(size[0], size[1], value))
		node = wx.StaticBitmap(self.parent, size=size)
		if bgColor:
			node.SetBackgroundColour(bgColor)
		if rgb:
			node.SetBitmap(wx.Bitmap.FromBuffer(size[0], size[1], rgb))
		if bind:
			bind.bind(_setBipmap)
		return (node, flag, border)

	def Node(self, node, flag=0, border=0):
		return (node, flag, border)

# 信号，仿solidjs，实现数据绑定后的自动更新
class Signal:
	def __init__(self, value, defaultValue=None, onsetter=None, callback=None):
		self._value = value
		if defaultValue != None:
			self._defaultValue = defaultValue
		self._onsetter = onsetter
		self._callbacks = []
		if callback != None:
			self._callbacks.append(callback)
	
	def __call__(self):
		return self._value

	@property
	def value(self):
		return self._value

	@value.setter
	def value(self, new_value):
		# 检查new_value是否为NumPy数组
		if isinstance(new_value, np.ndarray):
			# 处理数组值的逻辑（例如，直接赋值）
			self._value = new_value
		else:
			# 原有的逻辑
			if new_value == None and hasattr(self, "_defaultValue"):
				self._value = self._defaultValue
			else:
				self._value = new_value
		for callback in self._callbacks:
			callback(new_value)

	def bind(self, callback):
		self._callbacks.append(callback)

# 测试
# class App(wx.App):
# 	def OnInit(self):
# 		self.frame = MainFrame(None)
# 		self.frame.Show()
# 		return True

# class MainFrame(wx.Frame):
# 	def __init__(self, parent):
# 		wx.Frame.__init__(self, parent, -1, "")
# 		panel = wx.Panel(self)
# 		ui = UI(panel)
# 		id = Signal("hello1")
# 		toggle = Signal(True)
# 		layout = ui.Box(wx.VERTICAL, [
# 			ui.Label("Hello", bind=id),
# 			ui.Label("World"),
# 			ui.CheckBox("CheckBox", bind=toggle),
# 			ui.Button("Click Me", onclick=(lambda evt: print("click!", toggle.value))),
# 			ui.TextEntry("input", onchange=(lambda evt: print(evt.GetString()))),
# 		])
		
# 		panel.SetSizer(layout[0])
	
# app = App()
# app.MainLoop()